import {
  addRole,
  getAllApi,
  getAllMenu,
  getRoles,
  removeRole,
  updateRole,
} from '@/services/ant-design-pro/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { DrawerForm, PageContainer, ProCard, ProTable } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { Button, Input, message, Pagination, Popover, TabsProps, Tree } from 'antd';
import { DataNode } from 'antd/es/tree';
import React, { useRef, useState } from 'react';
import EditForm from './componets/EditForm';

type TreeNode = DataNode & { parentId?: number; parent?: TreeNode };

const fillMenuChildren = (
  arr: API.MenuItem[],
  map: Map<number, TreeNode>,
  keys: number[],
  node?: TreeNode[],
) => {
  arr.forEach((e) => {
    const n: TreeNode = { key: e.id, title: e.title, children: [], parentId: e.parentId };
    node?.push(n);
    map.set(e.id, n);
    keys.push(e.id);
    if (e.routes && e.routes?.length > 0) {
      fillMenuChildren(e.routes, map, keys, n.children);
    }
  });
};

const fillApiChildren = (arr: API.Api[], keys: number[], node?: TreeNode[]) => {
  arr.forEach((e) => {
    const n: TreeNode = { key: e.id || 0, title: e.description, children: [] };
    node?.push(n);
    keys.push(e.id as number);
    if (e.children && e.children?.length > 0) {
      e.children.forEach((c) => {
        const n1: TreeNode = { key: c.id || 0, title: c.description, parent: n };
        n.children?.push(n1);
        keys.push(c.id as number);
      });
    }
  });
};

const fillParent = (map: Map<number, TreeNode>, node?: TreeNode[]) => {
  node?.forEach((e) => {
    const menu = map.get(e.key as number) as TreeNode;
    if (menu.parentId) {
      e.parent = map.get(menu.parentId);
    }
    if (e.children && e.children.length > 0) {
      fillParent(map, e.children);
    }
  });
};

const fixMenuIds = (map: Map<number, TreeNode>, keys: number[], remove: boolean) => {
  let menuIds: number[] = [];
  keys.forEach((k) => {
    const node = map.get(k as number);
    if (remove && node?.children?.some((c) => !keys.includes(c.key as number))) {
      menuIds.push(k);
    }
    if (!remove && keys.includes(k) && node?.parentId) {
      menuIds.push(node?.parentId as number);
    }
  });

  function removeMenuIds() {
    menuIds.forEach((e) => {
      const i = keys.indexOf(e);
      if (i > -1) {
        keys.splice(i, 1);
      }
    });
    return keys;
  }

  function addMenuIds() {
    menuIds.forEach((e) => {
      const i = keys.indexOf(e);
      if (i === -1) {
        keys.push(e);
      }
    });
    return keys;
  }

  return remove ? removeMenuIds() : addMenuIds();
};

const filterTreeNode = (
  e: React.ChangeEvent<HTMLInputElement>,
  treeData: TreeNode[],
  setExpandedKeys: React.Dispatch<React.SetStateAction<number[]>>,
) => {
  const searchValue = e.target.value;
  const regExp: RegExp = new RegExp(searchValue, 'g');
  const result: Set<number> = new Set();

  function findMatchNode(node: TreeNode) {
    if (regExp.test(node.title?.toString() || '')) {
      result.add(node.key as number);
      let parent = node.parent;
      while (parent) {
        result.add(parent.key as number);
        parent = parent.parent;
      }
    }
    if (node.children) {
      node.children.forEach((child: TreeNode) => findMatchNode(child));
    }
  }

  treeData.forEach((node) => findMatchNode(node));

  setExpandedKeys(Array.from(result));
};

function renderFilteredNodes(nodes: TreeNode[], expandedKeys: number[]): TreeNode[] {
  if (expandedKeys.length <= 0) {
    return nodes;
  }

  return nodes
    .filter((node) => expandedKeys.includes(node.key as number))
    .map((node) => {
      if (node.children) {
        return {
          ...node,
          children: renderFilteredNodes(node.children, expandedKeys),
        };
      }
      return node;
    });
}

/**
 * @en-US Add role
 * @zh-CN 添加角色
 * @param fields
 */
const handleAdd = async (fields: API.RoleItem) => {
  const hide = message.loading('正在添加');
  try {
    await addRole({ ...fields });
    hide();
    message.success('Added successfully');
    return true;
  } catch (error) {
    hide();
    message.error('Adding failed, please try again!');
    return false;
  }
};

/**
 * @en-US Update role
 * @zh-CN 更新角色
 * @param fields
 * @returns
 */
const handleUpdate = async (fields: API.RoleItem) => {
  const hide = message.loading('正在更新');
  try {
    await updateRole({ ...fields });
    hide();
    message.success('Updated successfully');
    return true;
  } catch (error) {
    hide();
    message.error('Updating failed, please try again!');
    return false;
  }
};

/**
 * @en-US Delete role
 * @zh-CN 删除角色
 * @returns
 */
const handleDelete = async (fields: API.RoleItem) => {
  const hide = message.loading('正在删除');
  try {
    await removeRole({ ...fields });
    hide();
    message.success('Deleted successfully');
    return true;
  } catch (error) {
    hide();
    message.error('Deleting failed, please try again!');
    return false;
  }
};

const RoleList: React.FC = () => {
  /**
   * @en-US International configuration
   * @zh-CN 国际化配置
   * */
  const intl = useIntl();

  const actionRef = useRef<ActionType>();

  /**
   * @en-US Pop-up window of new window
   * @zh-CN 新建窗口的弹窗
   *  */
  const [createModalOpen, handleModalOpen] = useState<boolean>(false);

  /**
   * @en-US Pop-up window of edit window
   * @zh-CN 编辑窗口的弹窗
   *  */
  const [setModalOpen, handleSetModalOpen] = useState<boolean>(false);

  /**
   * @en-US Pop-up window of delete window
   * @zh-CN 更新窗口的弹窗
   *  */
  const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);

  const [currentRow, setCurrentRow] = useState<API.RoleItem>();

  const [menuMap, setMenuMap] = useState<Map<number, TreeNode>>(new Map());

  const [menuTreeData, setMenuTreeData] = useState<TreeNode[]>([]);

  const [menuExpandedKeys, setMenuExpandedKeys] = useState<number[]>([]);

  const [menuCheckKeys, setMenuCheckKeys] = useState<number[]>([]);

  const [apiTreeData, setAPITreeData] = useState<TreeNode[]>([]);

  const [apiExpandedKeys, setAPIExpandedKeys] = useState<number[]>([]);

  const [apiCheckKeys, setAPICheckKeys] = useState<number[]>([]);

  const columns: ProColumns<API.RoleItem>[] = [
    { title: '角色ID', dataIndex: 'id', fixed: 'left', valueType: 'digit' },
    { title: '角色名称', dataIndex: 'name', fixed: 'left' },
    {
      title: '操作',
      valueType: 'option',
      fixed: 'right',
      render: (_, record) => [
        <Button
          key="set"
          onClick={() => {
            setCurrentRow(record);
            handleSetModalOpen(true);
          }}
          type="primary"
        >
          设置权限
        </Button>,
        <Button
          key="edit"
          onClick={() => {
            setCurrentRow(record);
            handleUpdateModalOpen(true);
          }}
          type="primary"
        >
          更新
        </Button>,
        <Popover
          key="delete"
          content={
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
              <p style={{ marginBottom: 8 }}>您确定要删除这个角色吗？</p>
              <div style={{ display: 'flex', justifyContent: 'center' }}>
                <Button
                  type="primary"
                  danger
                  style={{ marginRight: 8 }}
                  onClick={async () => {
                    const success = await handleDelete(record);
                    if (success) {
                      if (actionRef.current) {
                        actionRef.current.reload();
                      }
                    }
                  }}
                >
                  确认
                </Button>
                <Button style={{ marginLeft: 8 }}>取消</Button>
              </div>
            </div>
          }
        >
          <Button type="primary" danger>
            删除
          </Button>
        </Popover>,
      ],
    },
  ];

  const items: TabsProps['items'] = [
    {
      key: 'menu',
      label: '设置菜单权限',
      children: (
        <ProCard>
          <Input.Search
            placeholder="搜索菜单"
            allowClear={true}
            onChange={(e) => {
              filterTreeNode(e, menuTreeData, setMenuExpandedKeys);
            }}
          />
          <Tree
            blockNode={true}
            checkable={true}
            expandedKeys={menuExpandedKeys}
            checkedKeys={menuCheckKeys}
            onCheck={(e) => {
              setMenuCheckKeys(e as number[]);
            }}
            treeData={renderFilteredNodes(menuTreeData, menuExpandedKeys)}
          />
        </ProCard>
      ),
    },
    {
      key: 'api',
      label: '设置API权限',
      children: (
        <ProCard>
          <Input.Search
            placeholder="搜索API"
            allowClear={true}
            onChange={(e) => {
              filterTreeNode(e, apiTreeData, setAPIExpandedKeys);
            }}
          />
          <Tree
            blockNode={true}
            checkable={true}
            expandedKeys={apiExpandedKeys}
            checkedKeys={apiCheckKeys}
            onCheck={(e) => {
              setAPICheckKeys(e as number[]);
            }}
            treeData={renderFilteredNodes(apiTreeData, apiExpandedKeys)}
          />
        </ProCard>
      ),
    },
  ];

  return (
    <PageContainer>
      <ProTable<API.RoleItem, API.PageParams>
        headerTitle="查询角色"
        actionRef={actionRef}
        rowKey="id"
        toolBarRender={() => [
          <Button
            type="primary"
            key="primary"
            onClick={() => {
              handleModalOpen(true);
            }}
          >
            <PlusOutlined /> 新建
          </Button>,
        ]}
        request={getRoles}
        columns={columns}
      />
      <Pagination showSizeChanger showQuickJumper hideOnSinglePage />
      <EditForm
        title="新建角色"
        open={createModalOpen}
        onOpenChange={handleModalOpen}
        onFinish={async (value) => {
          const success = await handleAdd(value as API.RoleItem);
          if (success) {
            handleModalOpen(false);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
      />
      <EditForm
        title="更新角色"
        open={updateModalOpen}
        onOpenChange={handleUpdateModalOpen}
        onFinish={async (value) => {
          if (currentRow) {
            currentRow.name = value.name;
          }
          const success = await handleUpdate(currentRow as API.RoleItem);
          if (success) {
            handleUpdateModalOpen(false);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
        values={currentRow}
      />
      <DrawerForm
        title="设置权限"
        open={setModalOpen}
        request={async () => {
          const data1: API.MenuItem[] = (await getAllMenu()).data as API.MenuItem[];
          const map: Map<number, TreeNode> = new Map();
          const nodes: TreeNode[] = [];
          const keys: number[] = [];
          fillMenuChildren(data1, map, keys, nodes);
          fillParent(map, nodes);
          setMenuMap(map);
          setMenuExpandedKeys(keys);
          setMenuCheckKeys(fixMenuIds(map, currentRow?.menuIds || [], true));
          setMenuTreeData(nodes);

          const data2: API.Api[] = await getAllApi();
          const nodes2: TreeNode[] = [];
          const keys2: number[] = [];
          fillApiChildren(data2, keys2, nodes2);
          setAPIExpandedKeys(keys2);
          setAPICheckKeys(currentRow?.apiIds || []);
          setAPITreeData(nodes2);

          const tmp: API.MenuItem = { id: 0 };
          return tmp;
        }}
        drawerProps={{
          title: '修改角色[' + currentRow?.name + ']权限',
          destroyOnClose: true,
          onClose: () => {
            handleSetModalOpen(false);
            setCurrentRow(undefined);
          },
        }}
        onFinish={async () => {
          if (currentRow) {
            currentRow.menuIds = fixMenuIds(menuMap, menuCheckKeys, false);
            currentRow.apiIds = apiCheckKeys && apiCheckKeys.filter((e) => e > 0);
          }
          const success = await handleUpdate(currentRow as API.RoleItem);
          if (success) {
            handleSetModalOpen(false);
            setCurrentRow(undefined);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
      >
        <ProCard
          tabs={{
            type: 'card',
            items: items,
          }}
        />
      </DrawerForm>
    </PageContainer>
  );
};

export default RoleList;
